#!/usr/bin/env python
# --*-- encoding:utf-8 --*-- 


'''
Created on 2011-12-31
@author: fredzhu.com
'''
import threading,urllib,urllib2,time,datetime
import os,random,sys
from django.conf import settings

reload(sys)

if not settings.DEBUG:
    sys.setdefaultencoding('utf8') 

try:
    import json
except:
    import simplejson as json

from fetion.cron import cron_check
from fetion.models import FetionStatus,FETION_STATUS_ENUM,SMSQueue,TaskCron

PRJ_PATH = os.path.abspath('.')

DOWNLOAD_ABST_DIR = u'static/code_imgs/%s'%(time.strftime('%Y/%m', time.localtime(time.time())))


class FetionWebIM():
    u'''飞信IM协议
    '''
    
    def __init__(self,logger,data={}):
        self.logger = logger
        self.data = data
        
    def login(self,phone,password,vcode):
        u'''登陆飞信
        '''
        POST = {
            "UserName":phone,
            "Pwd":password,
            "OnlineStatus":0,
            "Ccp":vcode
        }
        HEAD = {  
            'Host':'webim.feixin.10086.cn',
            'Cookie':self.data['ccpsession'],  
            'Referer':'https://webim.feixin.10086.cn/login.aspx',  
            'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
        }  
        req = urllib2.Request(u"https://webim.feixin.10086.cn/WebIM/Login.aspx", urllib.urlencode(POST), HEAD)
        resp = urllib2.urlopen(req)
        if resp.getcode()!=200:
            return
        rc = json.loads(resp.read())
        if rc['rc'] == 200:
            self.logger.info(u"%s 登录成功!"%phone)
            #登录成功,读取cookie信息
            info = resp.info()
            webim_sessionid = info['set-cookie'].split('webim_sessionid=')[1].split(';')[0]
            self.data['webim_sessionid'] = webim_sessionid
            self.data['phone'] = phone
            self.logger.info(u"获取会话ID:%s"%webim_sessionid)
            return 200
        elif rc['rc'] == 312:
            self.logger.warn(u"%s 验证码输入错误!"%phone)
            return 312
        elif rc['rc'] == 321:
            self.logger.warn(u"%s 密码输入错误!"%phone)
            return 321
        else:
            self.logger.warn(u"%s 未知错误!"%phone)  
            return 400
        
    def get_persion_info(self):
        POST = {
            'ssid':self.data['webim_sessionid']
        }
        req = urllib2.Request(u"http://webim.feixin.10086.cn/WebIM/GetPersonalInfo.aspx?Version=0", urllib.urlencode(POST))
        resp = urllib2.urlopen(req)        
        if resp.getcode()!=200:
            return json.loads({})
        data = json.loads(resp.read())
        
        self.logger.info(data)
        
        if data and data['rc'] == 200:
            rv = data['rv']
            self.logger.info(u"获取用户信息正常!%s"%rv)
            return rv
        else:
            self.logger.error(u"获取用户信息失败!")
            return json.loads({})

    def send_msg(self,to,msg,session_id):
        u'''发送短信
        '''
        HEAD = {  
            'Host':'webim.feixin.10086.cn',
            'Referer':'https://webim.feixin.10086.cn/login.aspx',  
            'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
        }
        
        POST = {
            "Message":msg.encode('utf-8'),
            "Receivers":to,
            "UserName":"1045701458",
            "ssid":session_id
        }
        
        req = urllib2.Request(u"http://webim.feixin.10086.cn/content/WebIM/SendSMS.aspx?Version=9", urllib.urlencode(POST) ,HEAD)
        resp = urllib2.urlopen(req)
        #返回信息
        rc = json.loads(resp.read())
        
        if rc['rc'] == 200:
            #发送成功
            self.logger.info(u"发给[%s]发送成功!"%to)
            return True
        return False

    def get_vcode_img(self,down_dir_path=DOWNLOAD_ABST_DIR):
        u'''获取图片验证码
        '''
        u = urllib2.urlopen("https://webim.feixin.10086.cn/WebIM/GetPicCode.aspx?Type=ccpsession")
        #读取cookie
        if u.getcode()==200:
            #先读取头信息
            header = u.info()
            #读取cookie
            cookie = header['set-cookie'].split(';')[0]
            self.data['ccpsession'] = cookie
            #下载图片
            if os.path.isdir(r'%s/%s'%(PRJ_PATH,down_dir_path)) == False:
                os.makedirs(r'%s/%s'%(PRJ_PATH,down_dir_path))
                
            file_path = r'%s/vcode_%s.jpeg'%(down_dir_path,"".join(random.sample(['1','2','3','4','5','6','7','8','9','0'], 5)))
            download_file_path = r'%s/%s'%(PRJ_PATH,file_path)
            downloaded_image = file(download_file_path.encode('utf-8'), "wb")
            try:
                while True:
                    buf = u.read(65536)
                    if len(buf) == 0:
                        break
                    downloaded_image.write(buf)
                downloaded_image.close()
                u.close()
                self.logger.debug('download webim vcode image success')
                return "/"+file_path
            except:
                self.logger.debug('download webim vcode image failture')
            #返回图片路径
            return "/"+file_path

    def get_contact_list(self):
        u'''获取用户联系人列表
        '''
        POST = {
            'ssid':self.data['webim_sessionid']
        }
        req = urllib2.Request(u"http://webim.feixin.10086.cn/WebIM/GetContactList.aspx?Version=1",urllib.urlencode(POST))
        resp = urllib2.urlopen(req)
        if resp.getcode()!=200:
            return
        data = json.loads(resp.read())
        if data and data['rc'] == 200:
            rv = data['rv']
            self.logger.info(u"获取联系人列表成功!%s"%rv)
            return rv
        else:
            self.logger.info(u"获取联系人列表失败!")
        resp.close()
            
    def start_thread(self):
        u'''启动辅助线程
        '''
        #1. 开启心跳线程,保持会话...
        h_thread = FetionHeartThread(self.logger,self.data)
        h_thread.start()
        #2. 轮循短信队列
        q_thread = FetionQueueThread(self.logger,self.data)
        q_thread.start()
        #3. 开启任务线程,定时执行任务
        t_thread = FetionTaskThread(self.logger,self.data)
        t_thread.start()
            

class FetionTaskThread(threading.Thread):
    u'''飞信任务线程
    负责定时把Task的任务放入短信队列里
    '''
    def __init__(self,logger,data):
        threading.Thread.__init__(self) 
        self.logger = logger
        self.phone = data['phone']
    
    def run(self):
        run = True
        while run:
            time.sleep(59)#略大约55秒,防止1分钟内存在2次触发
            self.logger.debug(u'轮循task......')
            list = TaskCron.objects.filter(phone=self.phone)
            for task in list:
                cron = task.cron
                if self.__check(cron):
                    self.logger.debug(u"cron[%s]符合条件......"%cron)
                    SMSQueue.addQueue(self.phone,task)
    
    def __check(self,cron):
        u'''检测cron是否可以运行
        '''
        return cron_check(datetime.datetime.now(), cron)
        
class FetionQueueThread(threading.Thread):
    u'''短信队列线程
    '''
    def __init__(self,logger,data,error_max=5):
        threading.Thread.__init__(self) 
        self.logger = logger
        self.phone = data['phone']
        self.webim_sessionid = data['webim_sessionid']
        self.webim = FetionWebIM(logger,data)
        self.error_count = 0#记录发送错误数
        self.error_max = error_max
        
    def run(self):
        run = True
        while run:
            time.sleep(10)#我就不信10秒还不能登录        
            run = self.__roll_queue()#轮循队列
            if not run:
                FetionStatus.update_status(self.phone)

    def __roll_queue(self):
        u'''轮循队列
        '''
        
        status = FetionStatus.objects.filter(phone=self.phone)
        #已经下线.停止线程
        if not status or status[0].status == FETION_STATUS_ENUM[1][0]:
            self.logger.debug(u"飞信[%s]下线,中止短信轮循......"%self.phone)
            return False
        
        self.logger.debug(u'轮循短信队列......')
        queues = SMSQueue.objects.filter(phone=self.phone)#只负责自己的
        if queues and len(queues)>0:
            for q in queues:
                receiver = q.receiver
                msg = q.msg
                try:
                    flag = self.webim.send_msg(receiver,msg, self.webim_sessionid)
                    if flag:
                        q.delete()#完成删除
                        self.logger.info(u'发送短信成功 to=%s'%receiver)
                        self.error_count = 0
                    else:
                        self.logger.error(u'发送短信失败! to=%s'%receiver)
                        self.error_count += 1
                except:
                    self.logger.error(u'发送短信失败! to=%s'%receiver)
                    self.error_count += 1
        #判断下
        if self.error_max >= self.error_count:
            return True  
        return False
    
class FetionHeartThread(threading.Thread):
    u'''保持飞信年轻健康的心跳
    '''
    
    def __init__(self,logger,data):
        threading.Thread.__init__(self) 
        self.logger = logger
        self.phone = data['phone']
        self.webim_sessionid = data['webim_sessionid']

    def run(self):
        run = True
        index = 0
        while run:
            run = self.__jump(index)#心跳
            index = index + 1
            time.sleep(30)
        self.logger.error(u"当前[%s]心跳已停止"%self.phone)


    def __jump(self,index=0):
        u'''一次心跳
        '''
        POST = {
            'ssid':self.webim_sessionid
        }
        req = urllib2.Request(u"http://webim.feixin.10086.cn/WebIM/GetConnect.aspx?Version=%s"%index,urllib.urlencode(POST))
        resp = urllib2.urlopen(req)
        data = json.loads(resp.read())
        self.logger.debug(u"心跳线程:%s"%data)
        #出状况了
        if resp.getcode()!=200:
            return False
        if data['rc'] == 200:
            self.logger.debug(data)
            for d in data['rv']:
                if d['DataType'] == 4:
                    #中断线程
                    self.logger.error(u"别处用户登录!心跳停止")
                    FetionStatus.update_status(self.phone)
                    return False
        resp.close()
        #设计有点问题
        return True
    

